Fix GH-22878: Use-after-free of callable via autoloader - #22881
Conversation
|
Aren't there already similar workarounds for these kinds of things? I recall a PR to add refcounting to closure objects etc. It's unfortunate that all these protections are:
|
|
You are not wrong, it is a bit of whack-a-mole. Same direction as the GH-20001 work, but this one can't fold into it. #22515/#22743 defer the reentrant code (error handlers, destructors); the trigger here is the autoloader, whose result class resolution needs synchronously, so there's nothing to defer, only the borrowed method string and receiver to hold across the lookup. The deprecation variant does overlap #22515. INIT_USER_CALL also builds its frame inline, so #22151's zend_call_function pin doesn't reach it either. |
Girgias
left a comment
There was a problem hiding this comment.
This should only target master.
Validating an array or string callable runs user code before its borrowed method name and object are used: a string class name can trigger an autoloader, and a compound "Class::method" name emits an E_DEPRECATED that reaches a user error handler. Either can free or mutate the callable, leaving the method string and $this dangling. Copy the method string before the reentrant lookup, and hold the callable array across INIT_USER_CALL's validation and frame build so the object survives to the call. This also covers call_user_func_array(), $cb(), and referenced or reference-wrapped array members. Fixes phpGH-22878
1e4375d to
5a8e1b5
Compare
|
Retargeted to master and rebased. |
A
['Class','method']or'Class::method'callable is validated by borrowing the method name and object out of the callable, then resolving the class. Resolving a string class can run an autoloader, and a compound name emits a deprecation that reaches a user error handler; either can free or mutate the callable mid-validation, so the borrowed method string and$thisdangle when the method is resolved and the call frame is built. This copies the borrowed string before the reentrant lookup and holds the array acrossINIT_USER_CALLso the object survives, coveringcall_user_func(),call_user_func_array(),$cb(), and referenced/reference-wrapped array callables.Fixes #22878